<!DOCTYPE html>
<html lang="zh-Hans">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.2.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/vhuichen.ico">
  <link rel="icon" type="image/png" sizes="32x32" href="/vhuichen.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/vhuichen.ico">
  <link rel="mask-icon" href="/vhuichen.ico" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"vhuichen.github.io","root":"/","images":"/images","scheme":"Pisces","darkmode":false,"version":"8.13.0","exturl":false,"sidebar":{"position":"left","display":"display","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Searching...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="第二章：对象、消息、运行期(3)第12条：理解消息转发机制当一个对象接收到无法解读的消息后，就会开启“消息转发”机制。如果消息转发也无法解读消息，程序就会抛出异常：  unrecognized selector sent to instance xxxx  消息转发分为两大阶段： 第一阶段：动态方法解析征询接受者能否动态添加方法来处理这个消息。此时会调用以下两个方法之一： 1234&#x2F;&#x2F; 以类方法调">
<meta property="og:type" content="article">
<meta property="og:title" content="《Effective Objective-C 2.0》第二章阅读笔记(3)">
<meta property="og:url" content="https://vhuichen.github.io/2018/04/2018/04/%E3%80%8AEffective%20Objective-C%202.0%E3%80%8B%E7%AC%AC%E4%BA%8C%E7%AB%A0%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0(3)/index.html">
<meta property="og:site_name" content="vhuichen">
<meta property="og:description" content="第二章：对象、消息、运行期(3)第12条：理解消息转发机制当一个对象接收到无法解读的消息后，就会开启“消息转发”机制。如果消息转发也无法解读消息，程序就会抛出异常：  unrecognized selector sent to instance xxxx  消息转发分为两大阶段： 第一阶段：动态方法解析征询接受者能否动态添加方法来处理这个消息。此时会调用以下两个方法之一： 1234&#x2F;&#x2F; 以类方法调">
<meta property="og:locale">
<meta property="og:image" content="http://ovsbvt5li.bkt.clouddn.com/18-4-24/55820526.jpg">
<meta property="og:image" content="http://ovsbvt5li.bkt.clouddn.com/18-4-24/29661179.jpg">
<meta property="og:image" content="http://ovsbvt5li.bkt.clouddn.com/18-4-24/58460381.jpg">
<meta property="og:image" content="http://ovsbvt5li.bkt.clouddn.com/18-4-25/33685437.jpg">
<meta property="article:published_time" content="2018-04-22T16:00:00.000Z">
<meta property="article:modified_time" content="2018-07-01T08:17:08.912Z">
<meta property="article:author" content="vhuichen">
<meta property="article:tag" content="Effective Objective-C 2.0">
<meta property="article:tag" content="消息转发">
<meta property="article:tag" content="类对象本质">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://ovsbvt5li.bkt.clouddn.com/18-4-24/55820526.jpg">


<link rel="canonical" href="https://vhuichen.github.io/2018/04/2018/04/%E3%80%8AEffective%20Objective-C%202.0%E3%80%8B%E7%AC%AC%E4%BA%8C%E7%AB%A0%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0(3)/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-Hans","comments":true,"permalink":"https://vhuichen.github.io/2018/04/2018/04/%E3%80%8AEffective%20Objective-C%202.0%E3%80%8B%E7%AC%AC%E4%BA%8C%E7%AB%A0%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0(3)/","path":"2018/04/2018/04/《Effective Objective-C 2.0》第二章阅读笔记(3)/","title":"《Effective Objective-C 2.0》第二章阅读笔记(3)"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>《Effective Objective-C 2.0》第二章阅读笔记(3) | vhuichen</title>
  






  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">vhuichen</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">记录、分享</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section">Home</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section">Archives</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section">About</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%9A%E5%AF%B9%E8%B1%A1%E3%80%81%E6%B6%88%E6%81%AF%E3%80%81%E8%BF%90%E8%A1%8C%E6%9C%9F-3"><span class="nav-number">1.</span> <span class="nav-text">第二章：对象、消息、运行期(3)</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%AC%AC12%E6%9D%A1%EF%BC%9A%E7%90%86%E8%A7%A3%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E6%9C%BA%E5%88%B6"><span class="nav-number">1.1.</span> <span class="nav-text">第12条：理解消息转发机制</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5%EF%BC%9A%E5%8A%A8%E6%80%81%E6%96%B9%E6%B3%95%E8%A7%A3%E6%9E%90"><span class="nav-number">1.1.1.</span> <span class="nav-text">第一阶段：动态方法解析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%EF%BC%9A%E5%AE%8C%E6%95%B4%E7%9A%84%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E6%9C%BA%E5%88%B6"><span class="nav-number">1.1.2.</span> <span class="nav-text">第二阶段：完整的消息转发机制</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%A4%87%E7%94%A8%E6%8E%A5%E5%8F%97%E8%80%85"><span class="nav-number">1.1.2.1.</span> <span class="nav-text">备用接受者</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AE%8C%E6%95%B4%E7%9A%84%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91"><span class="nav-number">1.1.2.2.</span> <span class="nav-text">完整的消息转发</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E6%B5%81%E7%A8%8B%E5%9B%BE"><span class="nav-number">1.1.3.</span> <span class="nav-text">消息转发流程图</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Demo"><span class="nav-number">1.1.4.</span> <span class="nav-text">Demo</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%AC%AC13%E6%9D%A1%EF%BC%9A%E7%94%A8-%E2%80%9C%E6%96%B9%E6%B3%95%E8%B0%83%E9%85%8D%E6%8A%80%E6%9C%AF%E2%80%9D-%E8%B0%83%E8%AF%95-%E2%80%9C%E9%BB%91%E7%9B%92%E6%96%B9%E6%B3%95%E2%80%9D%EF%BC%88method-swizzling%EF%BC%89"><span class="nav-number">1.2.</span> <span class="nav-text">第13条：用 “方法调配技术” 调试 “黑盒方法”（method swizzling）</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%94%A8%E9%80%94"><span class="nav-number">1.2.1.</span> <span class="nav-text">用途</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%AC%AC14%E6%9D%A1%EF%BC%9A%E7%90%86%E8%A7%A3-%E2%80%9C%E7%B1%BB%E5%AF%B9%E8%B1%A1%E2%80%9D-%E7%9A%84%E6%9C%AC%E8%B4%A8"><span class="nav-number">1.3.</span> <span class="nav-text">第14条：理解 “类对象” 的本质</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BB%A7%E6%89%BF%E4%BD%93%E7%B3%BB%E5%9B%BE"><span class="nav-number">1.3.1.</span> <span class="nav-text">继承体系图</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9C%A8%E7%BB%A7%E6%89%BF%E4%BD%93%E7%B3%BB%E4%B8%AD%E6%9F%A5%E8%AF%A2%E7%B1%BB%E5%9E%8B%E4%BF%A1%E6%81%AF"><span class="nav-number">1.3.2.</span> <span class="nav-text">在继承体系中查询类型信息</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">1.3.3.</span> <span class="nav-text">总结</span></a></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="vhuichen"
      src="/images/vhuichen.jpg">
  <p class="site-author-name" itemprop="name">vhuichen</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">78</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">11</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">86</span>
        <span class="site-state-item-name">tags</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="Back to top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://vhuichen.github.io/2018/04/2018/04/%E3%80%8AEffective%20Objective-C%202.0%E3%80%8B%E7%AC%AC%E4%BA%8C%E7%AB%A0%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0(3)/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/vhuichen.jpg">
      <meta itemprop="name" content="vhuichen">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="vhuichen">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="《Effective Objective-C 2.0》第二章阅读笔记(3) | vhuichen">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          《Effective Objective-C 2.0》第二章阅读笔记(3)
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2018-04-23 00:00:00" itemprop="dateCreated datePublished" datetime="2018-04-23T00:00:00+08:00">2018-04-23</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">Edited on</span>
      <time title="Modified: 2018-07-01 16:17:08" itemprop="dateModified" datetime="2018-07-01T16:17:08+08:00">2018-07-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">In</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/iOS%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">iOS开发</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="第二章：对象、消息、运行期-3"><a href="#第二章：对象、消息、运行期-3" class="headerlink" title="第二章：对象、消息、运行期(3)"></a><center>第二章：对象、消息、运行期(3)</center></h1><h2 id="第12条：理解消息转发机制"><a href="#第12条：理解消息转发机制" class="headerlink" title="第12条：理解消息转发机制"></a>第12条：理解消息转发机制</h2><p>当一个对象接收到无法解读的消息后，就会开启“消息转发”机制。如果消息转发也无法解读消息，程序就会抛出异常：</p>
<blockquote>
<p>unrecognized selector sent to instance xxxx</p>
</blockquote>
<p>消息转发分为两大阶段：</p>
<h3 id="第一阶段：动态方法解析"><a href="#第一阶段：动态方法解析" class="headerlink" title="第一阶段：动态方法解析"></a>第一阶段：动态方法解析</h3><p>征询接受者能否动态添加方法来处理这个消息。此时会调用以下两个方法之一：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 以类方法调用时触发</span></span><br><span class="line">+ (<span class="type">BOOL</span>)resolveClassMethod:(SEL)sel</span><br><span class="line"><span class="comment">// 以实例方法调用时触发</span></span><br><span class="line">+ (<span class="type">BOOL</span>)resolveInstanceMethod:(SEL)sel</span><br></pre></td></tr></table></figure>
<p>如果需要在动态解析时处理消息，那么实现代码如下：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> run(<span class="type">id</span> <span class="keyword">self</span>, SEL _cmd) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@&quot;missRun -- run&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">+ (<span class="type">BOOL</span>)resolveInstanceMethod:(SEL)sel &#123;</span><br><span class="line">    <span class="keyword">if</span> (sel == <span class="built_in">NSSelectorFromString</span>(<span class="string">@&quot;missRun&quot;</span>)) &#123;</span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@&quot;sel == %@&quot;</span>,<span class="built_in">NSStringFromSelector</span>(sel));</span><br><span class="line">        class_addMethod([<span class="keyword">self</span> <span class="keyword">class</span>], sel, (IMP)run, <span class="string">&quot;v@:&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">YES</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [<span class="variable language_">super</span> resolveInstanceMethod:sel];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意这里 class_addMethod 的第一个参数是 [self superclass]</span></span><br><span class="line">+ (<span class="type">BOOL</span>)resolveClassMethod:(SEL)sel &#123;</span><br><span class="line">    <span class="keyword">if</span> (sel == <span class="built_in">NSSelectorFromString</span>(<span class="string">@&quot;missRun&quot;</span>)) &#123;</span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@&quot;sel == %@&quot;</span>,<span class="built_in">NSStringFromSelector</span>(sel));</span><br><span class="line">        class_addMethod([<span class="keyword">self</span> superclass], sel, (IMP)run, <span class="string">&quot;v@:&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">YES</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [<span class="variable language_">super</span> resolveInstanceMethod:sel];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>外部调用  </p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Person *person = [[Person alloc] init];</span></span><br><span class="line"><span class="comment">//[person performSelector:NSSelectorFromString(@&quot;missRun&quot;) withObject:nil];</span></span><br><span class="line"></span><br><span class="line">[Person performSelector:<span class="built_in">NSSelectorFromString</span>(<span class="string">@&quot;missRun&quot;</span>) withObject:<span class="literal">nil</span>];</span><br></pre></td></tr></table></figure>
<p>此时在外部调用 missRun 方法，最终将会访问 <strong>void run(id self, SEL _cmd)</strong> 方法。</p>
<blockquote>
<p>IMP 指向的函数必须要有 <strong>id self, SEL _cmd</strong> 这两个参数。  </p>
</blockquote>
<p>class_addMethod 的最后一个参数 <strong>“v@:”</strong> 中，v 表示返回值 void ， @ 表示第一个参数类型为 id ，: 表示 SEL 。具体可看文档 <a target="_blank" rel="noopener" href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html">Type Encodings</a></p>
<h3 id="第二阶段：完整的消息转发机制"><a href="#第二阶段：完整的消息转发机制" class="headerlink" title="第二阶段：完整的消息转发机制"></a>第二阶段：完整的消息转发机制</h3><p>接受者尝试能否将这条消息转发给其他接受者接收，如果不行就启用“完整的消息转发”。</p>
<h4 id="备用接受者"><a href="#备用接受者" class="headerlink" title="备用接受者"></a>备用接受者</h4><p>此时会调用下面的方法  </p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="type">id</span>)forwardingTargetForSelector:(SEL)aSelector &#123;</span><br><span class="line">    Sutdent *student = [[Sutdent alloc] init];</span><br><span class="line">    <span class="keyword">if</span> ([student respondsToSelector:aSelector]) &#123;</span><br><span class="line">        <span class="keyword">return</span> student;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [<span class="variable language_">super</span> forwardingTargetForSelector:aSelector];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="完整的消息转发"><a href="#完整的消息转发" class="headerlink" title="完整的消息转发"></a>完整的消息转发</h4><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="built_in">NSMethodSignature</span> *)methodSignatureForSelector:(SEL)aSelector &#123;</span><br><span class="line">    <span class="keyword">if</span> (aSelector == <span class="built_in">NSSelectorFromString</span>(<span class="string">@&quot;missRun&quot;</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> [<span class="built_in">NSMethodSignature</span> signatureWithObjCTypes:<span class="string">&quot;v@:&quot;</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [<span class="variable language_">super</span> methodSignatureForSelector:aSelector];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="type">void</span>)forwardInvocation:(<span class="built_in">NSInvocation</span> *)anInvocation &#123;</span><br><span class="line">    <span class="keyword">if</span> ([anInvocation selector] == <span class="built_in">NSSelectorFromString</span>(<span class="string">@&quot;missRun&quot;</span>)) &#123;</span><br><span class="line">        Sutdent *student = [[Sutdent alloc] init];</span><br><span class="line">        [anInvocation invokeWithTarget:student];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>“备用接受者”和“完整的消息转发”区别在于，“完整的消息转发”中可以改变消息的内容。</p>
<h3 id="消息转发流程图"><a href="#消息转发流程图" class="headerlink" title="消息转发流程图"></a>消息转发流程图</h3><p><img src="http://ovsbvt5li.bkt.clouddn.com/18-4-24/55820526.jpg"><br>接受者在每一步均有机会处理消息，越到最后，处理的代价会越高。</p>
<h3 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a>Demo</h3><p><a target="_blank" rel="noopener" href="https://github.com/vhuichen/MessageForwarding.git">GitHub: MessageForwarding</a></p>
<h2 id="第13条：用-“方法调配技术”-调试-“黑盒方法”（method-swizzling）"><a href="#第13条：用-“方法调配技术”-调试-“黑盒方法”（method-swizzling）" class="headerlink" title="第13条：用 “方法调配技术” 调试 “黑盒方法”（method swizzling）"></a>第13条：用 “方法调配技术” 调试 “黑盒方法”（method swizzling）</h2><p>类对象的方法列表会将“方法名”映射带相应的方法实现上，“动态消息派发系统”会根据这个表找到相应的方法。这些方法均以函数指针的方式表示。这种指针就是 IMP 。下图是 NSString 的部分方法映射表。<br><img src="http://ovsbvt5li.bkt.clouddn.com/18-4-24/29661179.jpg"><br>Objective-C 运行时系统提供了几个方法可以用来操作这张表。开发者可以在运行时新增方法，改变方法对应的实现，也可以交换两个方法的具体实现。例如我们可以让方法映射表变成下图这样<br><img src="http://ovsbvt5li.bkt.clouddn.com/18-4-24/58460381.jpg"><br>实现起来也是很简单的，创建一个 NSString 的分类，在 +load 方法中实现  </p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">+ (<span class="type">void</span>)load &#123;</span><br><span class="line">    Method originalMethod = class_getInstanceMethod([<span class="built_in">NSString</span> <span class="keyword">class</span>], <span class="keyword">@selector</span>(lowercaseString));</span><br><span class="line">    Method swappedMethod = class_getInstanceMethod([<span class="built_in">NSString</span> <span class="keyword">class</span>], <span class="keyword">@selector</span>(uppercaseString));</span><br><span class="line">    method_exchangeImplementations(originalMethod, swappedMethod);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>调用</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSString</span> *string = <span class="string">@&quot;This is a String&quot;</span>;</span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@&quot;lowercaseString = %@&quot;</span>,string.lowercaseString);</span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@&quot;uppercaseString = %@&quot;</span>,string.uppercaseString);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出</span></span><br><span class="line"><span class="comment">// lowercaseString = THIS IS A STRING</span></span><br><span class="line"><span class="comment">// uppercaseString = this is a string</span></span><br></pre></td></tr></table></figure>
<p>此时 lowercaseString 和 uppercaseString 的方法实现已经替换过来了。<br>lowercaseString 方法对应的是 uppercaseString 的方法实现。<br>uppercaseString 方法对应的是 lowercaseString 的方法实现。<br>所以打印出来的log是反过来的。当然这个没有什么意义。  </p>
<p>下面实现一个功能：每次调用 lowercaseString 都打印出相应的log出来  </p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">+ (<span class="type">void</span>)load &#123;</span><br><span class="line">    Method originalMethod = class_getInstanceMethod([<span class="built_in">NSString</span> <span class="keyword">class</span>], <span class="keyword">@selector</span>(lowercaseString));</span><br><span class="line">    Method swappedMethod = class_getInstanceMethod([<span class="built_in">NSString</span> <span class="keyword">class</span>], <span class="keyword">@selector</span>(vch_lowercaseString));</span><br><span class="line">    method_exchangeImplementations(originalMethod, swappedMethod);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="built_in">NSString</span> *)vch_lowercaseString &#123;</span><br><span class="line">    <span class="built_in">NSString</span> *string = [<span class="keyword">self</span> vch_lowercaseString];</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@&quot;----%@&quot;</span>,string);</span><br><span class="line">    <span class="keyword">return</span> string;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>调用</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSString</span> *string = <span class="string">@&quot;This is a String&quot;</span>;</span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@&quot;lowercaseString = %@&quot;</span>,string.lowercaseString);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出</span></span><br><span class="line"><span class="comment">// ----this is a string</span></span><br><span class="line"><span class="comment">// lowercaseString = this is a string</span></span><br></pre></td></tr></table></figure>
<p>由于 lowercaseString 和 vch_lowercaseString 交换了方法实现，所以当我们调用 lowercaseString 方法的时候，执行的是 vch_lowercaseString 里面的方法。所以才会打印出 log 出来。  </p>
<h3 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h3><p><strong>使用 method swizzling “黑魔法”，开发者可以在原有实现中添加新的功能。</strong></p>
<h2 id="第14条：理解-“类对象”-的本质"><a href="#第14条：理解-“类对象”-的本质" class="headerlink" title="第14条：理解 “类对象” 的本质"></a>第14条：理解 “类对象” 的本质</h2><p>看看下面的两个语句  </p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSString</span> *string0 = <span class="string">@&quot;this is a string&quot;</span>;</span><br><span class="line"><span class="type">id</span> string1 = <span class="string">@&quot;this is a string&quot;</span>;</span><br></pre></td></tr></table></figure>
<p>两个语句都创建了一个 NSSring 类型的对象，在编译时，编译器会将 string0 按照 NSString 类型来检测，string1 按照 id 类型来检测。string0 直接调用 NSString 的方法编译器不会报错，string1 直接调用 NSString 的方法则编译器报错。 而在运行时两个对象表示的意思是一样的。</p>
<p>在 objc.h 中是这样定义 id 类型的</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// objc.h</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/// An opaque type that represents an Objective-C class.</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_class *Class;</span><br><span class="line"></span><br><span class="line"><span class="comment">/// Represents an instance of a class.</span></span><br><span class="line"><span class="keyword">struct</span> objc_object &#123;</span><br><span class="line">    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/// A pointer to an instance of a class.</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_object *<span class="type">id</span>;</span><br></pre></td></tr></table></figure>
<p>可以看出 id 是 objc_object 结构体类型的指针，objc_object 包含了一个 Class 类型的变量 isa ，Class 是 objc_class 类型的指针。<br>再看看 NSObject.h 中的定义</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// NSObject.h</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">NSObject</span> &lt;<span class="title">NSObject</span>&gt; </span>&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> clang diagnostic push</span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> clang diagnostic ignored <span class="string">&quot;-Wobjc-interface-ivars&quot;</span></span></span><br><span class="line">    Class isa  OBJC_ISA_AVAILABILITY;</span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> clang diagnostic pop</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里面包含了一个 Class 类型的变量 isa 。这个 Class 也就是 objc_class 类型的指针。<br>事实上每个实例变量都会包含一个 objc_object 结构体，该结构体的第一个成员变量就是 isa 指针。既然是指针，那么 objc_class 也是一个对象，我们称之为“类对象”，这个类对象是一个单例，程序运行中只存在一份。   </p>
<p>再看看 runtime.h 是怎么定义 objc_class 结构体的。  </p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// runtime.h</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> objc_class &#123;</span><br><span class="line">    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !__OBJC2__</span></span><br><span class="line">    Class _Nullable super_class                              OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="keyword">const</span> <span class="type">char</span> * _Nonnull name                               OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="type">long</span> version                                             OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="type">long</span> info                                                OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="type">long</span> instance_size                                       OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="keyword">struct</span> objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="keyword">struct</span> objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="keyword">struct</span> objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;</span><br><span class="line">    <span class="keyword">struct</span> objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">&#125; OBJC2_UNAVAILABLE;</span><br></pre></td></tr></table></figure>
<p>objc_class 的第一个成员变量也是 isa 指针。它指向的是类的元类（metaclass）。objc_class 负责保存类的实例变量、方法列表、缓存方法列表、协议列表等。元类（metaclass）则负责保存类方法列表。</p>
<h3 id="继承体系图"><a href="#继承体系图" class="headerlink" title="继承体系图"></a>继承体系图</h3><p><img src="http://ovsbvt5li.bkt.clouddn.com/18-4-25/33685437.jpg"><br>每一个实例对象都有一个 isa 指针指向其类对象，用来表明其类型，类对象也有一个 isa 指针，指向其元类，元类同样存在一个 isa 指针，指向其根元类，根元类的 isa 指针则指向自身。这些类对象则构成了类的继承体系。</p>
<h3 id="在继承体系中查询类型信息"><a href="#在继承体系中查询类型信息" class="headerlink" title="在继承体系中查询类型信息"></a>在继承体系中查询类型信息</h3><p><strong>isMemberOfClass</strong>  不包含父类，用来判断是否是某个特定类的实例。（需要考虑“类族”）<br><strong>isKindOfClass</strong>    包含父类，用来判断是否是某个特定类或者派生类的实例。  </p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>1、类本质也是一个对象（类对象）。<br>2、类对象会在程序第一次使用时创建一次，是个单例。<br>3、类对象是一种数据结构。存储了类的版本、描述信息、大小、变量列表、方法列表、方法缓存、协议列表等。<br>4、元类中保存了类方法列表。</p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/Effective-Objective-C-2-0/" rel="tag"># Effective Objective-C 2.0</a>
              <a href="/tags/%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91/" rel="tag"># 消息转发</a>
              <a href="/tags/%E7%B1%BB%E5%AF%B9%E8%B1%A1%E6%9C%AC%E8%B4%A8/" rel="tag"># 类对象本质</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2018/04/2018/04/%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96/" rel="prev" title="尾调用优化">
                  <i class="fa fa-chevron-left"></i> 尾调用优化
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2018/04/2018/04/%E3%80%8AEffective%20Objective-C%202.0%E3%80%8B%E7%AC%AC%E4%B8%89%E7%AB%A0%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/" rel="next" title="《Effective Objective-C 2.0》第三章阅读笔记">
                  《Effective Objective-C 2.0》第三章阅读笔记 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 2015 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">vhuichen</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/pisces/" rel="noopener" target="_blank">NexT.Pisces</a>
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
